package cn.dansj.common.utils.json;

import cn.dansj.common.utils.transfer.NameStyleUtils;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonToken;
import com.fasterxml.jackson.core.ObjectCodec;
import com.fasterxml.jackson.databind.BeanDescription;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.deser.std.DelegatingDeserializer;
import com.fasterxml.jackson.databind.introspect.AnnotatedField;
import com.fasterxml.jackson.databind.node.JsonNodeFactory;
import com.fasterxml.jackson.databind.node.ObjectNode;

import java.io.IOException;
import java.util.Iterator;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

class SmartFieldNameDeserializer extends DelegatingDeserializer {
    private final Map<String, String> fieldNameMappings = new ConcurrentHashMap<>();
    private final BeanDescription beanDesc;

    public SmartFieldNameDeserializer(JsonDeserializer<?> delegate, BeanDescription beanDesc) {
        super(delegate);
        this.beanDesc = beanDesc;
        initFieldMappings();
    }

    private void initFieldMappings() {
        beanDesc.findProperties().forEach(prop -> {
            String originalName = prop.getName();
            AnnotatedField annotatedField = prop.getField();
            String fieldName = annotatedField == null ? originalName : annotatedField.getName();
            if (originalName.equals(fieldName)) {
                putFieldMapping(originalName, originalName);
            } else {
                putFieldMapping(fieldName, fieldName);
                putFieldMapping(originalName, fieldName);
            }
        });
    }

    private void putFieldMapping(String fieldName, String originalName) {
        for (String convert : NameStyleUtils.convertTo(fieldName)) {
            fieldNameMappings.put(convert, originalName);
        }
    }

    @Override
    protected JsonDeserializer<?> newDelegatingInstance(JsonDeserializer<?> newDelegatee) {
        return new SmartFieldNameDeserializer(newDelegatee, beanDesc);
    }

    @Override
    public Object deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
        if (p.currentToken() == JsonToken.START_OBJECT) {
            JsonNode node = p.readValueAsTree();
            ObjectCodec codec = p.getCodec();
            ObjectNode mappedNode = JsonNodeFactory.instance.objectNode();
            Iterator<Map.Entry<String, JsonNode>> fields = node.fields();
            while (fields.hasNext()) {
                Map.Entry<String, JsonNode> entry = fields.next();
                String jsonFieldName = entry.getKey();
                String matchedFieldName = findBestMatch(jsonFieldName);
                if (matchedFieldName != null) {
                    mappedNode.set(matchedFieldName, entry.getValue());
                }
            }
            JsonParser newParser = mappedNode.isEmpty() ? node.traverse(codec) : mappedNode.traverse(codec);
            newParser.nextToken();
            return super.deserialize(newParser, ctxt);
        }
        return super.deserialize(p, ctxt);
    }

    private String findBestMatch(String jsonFieldName) {
        // 1. 精确匹配
        if (fieldNameMappings.containsKey(jsonFieldName)) {
            return fieldNameMappings.get(jsonFieldName);
        }
        // 2. 忽略大小写匹配
        String lowerCaseName = jsonFieldName.toLowerCase();
        for (Map.Entry<String, String> entry : fieldNameMappings.entrySet()) {
            if (entry.getKey().toLowerCase().equals(lowerCaseName)) {
                return entry.getValue();
            }
        }
        // 3. 下划线转驼峰匹配
        String camelCaseName = underscoreToCamel(jsonFieldName);
        if (fieldNameMappings.containsKey(camelCaseName)) {
            return fieldNameMappings.get(camelCaseName);
        }
        return null;
    }

    private String underscoreToCamel(String name) {
        if (name == null || name.isEmpty()) return name;
        StringBuilder result = new StringBuilder();
        boolean nextUpper = false;
        for (int i = 0; i < name.length(); i++) {
            char c = name.charAt(i);
            if (c == '_') {
                nextUpper = true;
            } else {
                if (nextUpper) {
                    result.append(Character.toUpperCase(c));
                    nextUpper = false;
                } else {
                    result.append(c);
                }
            }
        }
        return result.toString();
    }
}